#ifndef _OM_EXCEPTION_H_
#define _OM_EXCEPTION_H_

#include "OmPrerequisite.h"

#include "OmString.h"

#include <exception>

namespace OmPublic 
{
	class OmExport Exception : public std::exception
	{
		public:
			enum ExceptionCodes 
			{
				ERR_CANNOT_WRITE_TO_FILE,
				ERR_INVALID_STATE,
				ERR_INVALIDPARAMS,
				ERR_RENDERINGAPI_ERROR,
				ERR_DUPLICATE_ITEM,
				ERR_ITEM_NOT_FOUND,
				ERR_FILE_NOT_FOUND,
				ERR_INTERNAL_ERROR,
				ERR_RT_ASSERTION_FAILED, 
				ERR_NOT_IMPLEMENTED
			};
			Exception(int number, const String & description, const String & source);
			Exception(int number, const String & description, const String & source, const TCHAR * type, const char * file, long line);
			Exception(const Exception &);
			~Exception() throw(){}

			void operator = (const Exception &);
			virtual const String & getFullDescription() const;
			virtual int getNumber() const throw ();
			const String & getDescription() const {return mDescription;}

		protected:
			int mNumber;
			long mLine;
			String mFile;
			String mSource;
			String mTypename;
			String mDescription;
			mutable String mFullDesc;
	};

	template <int num>
	struct ExceptionCodeType
	{
		enum { number = num };
	};


	class UnimplementedException : public Exception
	{
	public:
		UnimplementedException(int number, const String & description, const String & source, const char * file, long line) :
			Exception(number, description, source, _T("UnimplementedException"), file, line){}
	};


	class FileNotFoundException : public Exception
	{
	public:
		FileNotFoundException(int number, const String & description, const String & source, const char * file, long line) :
		  Exception(number, description, source, _T("FileNotFoundException"), file, line){}
	};

	class IOException : public Exception
	{
	public:
		IOException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("IOException"), file, line) {}
	};
	class InvalidStateException : public Exception
	{
	public:
		InvalidStateException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("InvalidStateException"), file, line) {}
	};
	class InvalidParametersException : public Exception
	{
	public:
		InvalidParametersException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("InvalidParametersException"), file, line) {}
	};
	class ItemIdentityException : public Exception
	{
	public:
		ItemIdentityException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("ItemIdentityException"), file, line) {}
	};
	class InternalErrorException : public Exception
	{
	public:
		InternalErrorException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("InternalErrorException"), file, line) {}
	};
	class RenderingAPIException : public Exception
	{
	public:
		RenderingAPIException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("RenderingAPIException"), file, line) {}
	};
	class RuntimeAssertionException : public Exception
	{
	public:
		RuntimeAssertionException(int number, const String& description, const String& source, const char* file, long line)
			: Exception(number, description, source, _T("RuntimeAssertionException"), file, line) {}
	};

	class ExceptionFactory
	{
	private:
		/// Private constructor, no construction
		ExceptionFactory() {}
	public:
		static UnimplementedException create(
			ExceptionCodeType<Exception::ERR_NOT_IMPLEMENTED> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return UnimplementedException(code.number, desc, src, file, line);
		}
		static FileNotFoundException create(
			ExceptionCodeType<Exception::ERR_FILE_NOT_FOUND> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return FileNotFoundException(code.number, desc, src, file, line);
		}
		static IOException create(
			ExceptionCodeType<Exception::ERR_CANNOT_WRITE_TO_FILE> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return IOException(code.number, desc, src, file, line);
		}
		static InvalidStateException create(
			ExceptionCodeType<Exception::ERR_INVALID_STATE> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return InvalidStateException(code.number, desc, src, file, line);
		}
		static InvalidParametersException create(
			ExceptionCodeType<Exception::ERR_INVALIDPARAMS> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return InvalidParametersException(code.number, desc, src, file, line);
		}
		static ItemIdentityException create(
			ExceptionCodeType<Exception::ERR_ITEM_NOT_FOUND> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return ItemIdentityException(code.number, desc, src, file, line);
		}
		static ItemIdentityException create(
			ExceptionCodeType<Exception::ERR_DUPLICATE_ITEM> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return ItemIdentityException(code.number, desc, src, file, line);
		}
		static InternalErrorException create(
			ExceptionCodeType<Exception::ERR_INTERNAL_ERROR> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return InternalErrorException(code.number, desc, src, file, line);
		}
		static RenderingAPIException create(
			ExceptionCodeType<Exception::ERR_RENDERINGAPI_ERROR> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return RenderingAPIException(code.number, desc, src, file, line);
		}
		static RuntimeAssertionException create(
			ExceptionCodeType<Exception::ERR_RT_ASSERTION_FAILED> code, 
			const String& desc, 
			const String& src, const char* file, long line)
		{
			return RuntimeAssertionException(code.number, desc, src, file, line);
		}

	};

	#ifndef OM_EXCEPT
	#define OM_EXCEPT(num, desc, src) throw OmPublic::ExceptionFactory::create( \
		OmPublic::ExceptionCodeType<num>(), desc, src, __FILE__, __LINE__ );
	#endif
}
	
#endif